#pragma once

#include "LeDocumentObject_global.h"

#include <QObject>
#include <QUuid>

// TODO: rename to _LDO_SET_...

// eg. member = value; // set "name" property
#define _IDO_SET_PROPERTY3(name, value, member) \
    do { \
    beginSetProperty(#name); \
    member = value; \
    endSetProperty(#name); \
    } while (0)

// eg. m_name = value; // set "name" property
#define _IDO_SET_PROPERTY2(name, value) \
    _IDO_SET_PROPERTY3(name, value, m_##name)

// eg. m_name = name; // set "name" property
#define _IDO_SET_PROPERTY(name) \
    _IDO_SET_PROPERTY2(name, name)

namespace LDO
{

    class Document;
    class IDocumentObjectListener;


    // TBD: Get rid of type() since we can use qobject_cast and QMetatype::type("Object*")

    class IDocumentObjectPrivate;
    class LDO_EXPORT IDocumentObject: public QObject
    {
        Q_OBJECT

        Q_DISABLE_COPY(IDocumentObject)
        Q_DECLARE_PRIVATE(IDocumentObject)
        QScopedPointer<IDocumentObjectPrivate> const d_ptr;

        Q_PROPERTY(QString objectUserName READ objectUserName WRITE setObjectUserName NOTIFY objectUserNameChanged)
        Q_PROPERTY(QUuid objectId READ objectId DESIGNABLE false)

    public:
        enum
        {
            BaseType = 0x1000,
            GeometryBaseType = BaseType + 0x100,
            ArrangementBaseType = BaseType + 0x200,
            FeatureBaseType = BaseType + 0x300,
            MockBaseType = BaseType + 0x1000
        };

        explicit IDocumentObject(Document *document = nullptr);
        ~IDocumentObject();

        // FIXME:
        virtual bool hasEditor() const;

        QUuid objectId() const;
        QString objectUserName() const;
        int childObjectCount() const;
        int parentObjectIndex() const;
        IDocumentObject *childObject(int index) const;
        IDocumentObject *parentObject() const;
        void addChildObject(IDocumentObject *child);
        void removeChildObject(IDocumentObject *child);

        void registerListener(IDocumentObjectListener *listener);
        void unregisterListener(IDocumentObjectListener *listener);

        virtual int objectType() const { return BaseType; }

        template<class T>
        T* as()
        {
            return qobject_cast<T*>(this);
        }

    signals:
        void objectUserNameChanged(const QString &name);

    public slots:
        void setObjectUserName(const QString &name);

        // Move to IDocumentObjectPrivate
    protected:
        void beginAddChild(IDocumentObject *child, int index);
        void endAddChild(IDocumentObject *child, int index);
        void beginRemoveChild(IDocumentObject *child, int index);
        void endRemoveChild(IDocumentObject *child, int index);
        void beginSetProperty(const char *name);
        void endSetProperty(const char *name);

        // QObject interface
    public:
        virtual bool event(QEvent *event) override;
    };

}
